import { WaitForPushCardEvent, createDefaultEvent } from "@/store/reactiveStore";
import { BasicCard, BrilliantCard, Card, CardType, Equipment } from "../cardsystem/card";
import { Jiu, Sha, Shan, Tao } from "../cardsystem/basicCard";
import { WuXieKeJi } from "../cardsystem/brilliantness/nodelay";
import { Player } from "../playersystem/player";
import _ from 'lodash';


export enum PickPurpose {
    USING, // 使用

    DROPPING  // 弃牌

}

export interface PickCardOptions {
    canPickCards: Card[]
    canPickMin: number
    canPickMax: number
    pickPurpose: PickPurpose
    currentPlayer: Player
    resonCard?: Card

}

export interface PickPlayerOptions {
    canPickPlayers: Player[]
    canPickMin: number
    canPickMax: number
    currentPlayer: Player
}

export interface PickOtherCardOptions {
    player: Player
    pickRange?: CardType<Card>[]
    canPickMin: number
    canPickMax: number
    currentPlayer: Player
    resonCard?: Card
}

export interface BehaviorStragy {
    waitForPickCard(options: PickCardOptions): Promise<WaitForPushCardEvent>

    waitForPickPlayer(options: PickPlayerOptions): Promise<WaitForPushCardEvent>

    waitForPickAllTypeCard(options: PickOtherCardOptions): Promise<WaitForPushCardEvent>

    waitForResponseCard(options: PickCardOptions): Promise<WaitForPushCardEvent>
}

type PickCardFunc = (card: Card, player: Player) => boolean

export class BaseSmartBehavior implements BehaviorStragy {

    remainCardPriority: CardType<Card>[]

    cardUsePriority: PickCardFunc[]

    constructor() {
        this.remainCardPriority = [Tao, Jiu, Shan, WuXieKeJi, Sha]
        this.cardUsePriority = [(card, player) => card instanceof Tao, (card, player) => card instanceof Equipment, (card, player) => card instanceof BrilliantCard, (card, player) => {
            if (card instanceof Jiu) {
                return player.handCardAreas.hasCanuseCard(Sha)
            } else {
                return card instanceof BasicCard
            }
        }]
    }

    waitForPickCard(options: PickCardOptions): Promise<WaitForPushCardEvent> {
        if (options.pickPurpose == PickPurpose.USING) {
            return this.smartUseCardBehavior(options)
        } else {
            return this.smartDropCardBehavior(options)
        }
    }

    waitForResponseCard(options: PickCardOptions): Promise<WaitForPushCardEvent> {
        return new Promise((resolve) => {
            setTimeout(() => {
                const {canPickCards, canPickMax, canPickMin, currentPlayer, resonCard} = options
                const firstCard = canPickCards[0]
                const selectedCards = [firstCard]
                resolve(createDefaultEvent({selectedCards}))
            }, 2000)
        })
    }

    smartUseCardBehavior(options: PickCardOptions): Promise<WaitForPushCardEvent> {
        return new Promise((resolve) => {
            const {canPickCards, canPickMax, canPickMin, currentPlayer} = options
            const finalPickCards:Card[] = []

            for(const ct of this.cardUsePriority) {
                for (const card of canPickCards) {
                    if (ct(card, currentPlayer) && finalPickCards.length < canPickMax) {
                        finalPickCards.push(card)
                    }
                }
            }

            const gm = currentPlayer.gameManager
            gm.updateView()
            setTimeout(() => {
                const selectedCards = finalPickCards
                if (selectedCards.length > 0) {
                    resolve(createDefaultEvent({selectedCards}))
                } else {
                    resolve(createDefaultEvent({cancel:true}))
                }
            }, 2000)
        })
    }

    smartDropCardBehavior(options: PickCardOptions): Promise<WaitForPushCardEvent> {
        return new Promise((resolve) => {
            const {canPickCards, canPickMax, canPickMin} = options
            const finalPickCards:Card[] = []
            for(const card of canPickCards) {
                for (const dropType of this.remainCardPriority) {
                    if (!(card instanceof dropType) && finalPickCards.length < canPickMin) {
                        finalPickCards.push(card)
                    }
                }
            }

            if (finalPickCards.length < canPickMin) {
                const reversePriority = this.remainCardPriority.reverse()
                let pi = 0
                while (finalPickCards.length < canPickMin && pi < reversePriority.length) {
                    const dropType = reversePriority[pi++]
                    for(const card of canPickCards) {
                        if (card instanceof dropType && finalPickCards.length < canPickMin) {
                            finalPickCards.push(card)
                        }
                    }
                }
            }
            setTimeout(() => {
                const selectedCards = finalPickCards
                resolve(createDefaultEvent({selectedCards}))
            }, 2000)
        })
    }

    waitForPickPlayer(options: PickPlayerOptions): Promise<WaitForPushCardEvent> {
        return new Promise((resolve) => {
            const {canPickPlayers, canPickMax, canPickMin, currentPlayer} = options
            const finalPickPlayers:Player[] = []
            for(let i = 0; i < canPickMax && i < canPickPlayers.length; i++) {
                let index = _.random(0, canPickPlayers.length-1)
                const picked = canPickPlayers[index]
                if (picked.equals(currentPlayer)) {
                    index = (index+1) % canPickPlayers.length
                }
                finalPickPlayers.push(canPickPlayers[index])
            }
            setTimeout(() => {
                const selectedPlayers = finalPickPlayers
                if (selectedPlayers.length > 0) {
                    resolve(createDefaultEvent({selectedPlayers}))
                } else {
                    resolve(createDefaultEvent({cancel: true}))
                }
            }, 2000)
        })
    }

    waitForPickAllTypeCard(options: PickOtherCardOptions): Promise<WaitForPushCardEvent> {
        return new Promise((resolve) => {
            const {player, canPickMax, canPickMin} = options
            const dropEqPrior = [
                [() => player.equipmentAreas.hasShield(), () => player.equipmentAreas.getShield()],
                [() => player.equipmentAreas.hasPlusOneHourse(), () => player.equipmentAreas.getPlusOneHourse()],
                [() => player.equipmentAreas.hasWeapon, () => player.equipmentAreas.getWeapon()],
                [() => player.equipmentAreas.hasMinusOneHourse(), () => player.equipmentAreas.getPlusOneHourse()],
                [() => player.handCardAreas.hasHandCards(), () => player.handCardAreas.peekHandCard()]
            ]
            const selectedCards:Card[] = []
            for(let i = 0; i < canPickMax && i < dropEqPrior.length; i++) {
                const eqtup = dropEqPrior[i]
                if (eqtup[0]()) {
                    selectedCards.push(eqtup[1]() as Card)
                }
            }
            setTimeout(() => {

                if (selectedCards.length > 0) {
                    resolve(createDefaultEvent({selectedCards}))
                } else {
                    resolve(createDefaultEvent({cancel: true}))
                }
            }, 1000)
        })
    }

}

export class BaseUserBehavior implements BehaviorStragy {

    waitForPickCard(options: PickCardOptions): Promise<WaitForPushCardEvent> {
        const player = options.currentPlayer
        const gm = player.gameManager
        return gm.waitForPickCard(options.canPickCards, options.canPickMin, options.canPickMax)
    }

    waitForPickPlayer(options: PickPlayerOptions): Promise<WaitForPushCardEvent> {
        const player = options.currentPlayer
        const gm = player.gameManager
        return gm.waitForPickPlayer(options.canPickPlayers, options.canPickMin, options.canPickMax)
    }

    waitForPickAllTypeCard(options: PickOtherCardOptions): Promise<WaitForPushCardEvent> {
        const player = options.currentPlayer
        const gm = player.gameManager
        return gm.waitForPickAllTypeCard(options.player, options.canPickMin, options.canPickMax, options.pickRange)
    }

    waitForResponseCard(options: PickCardOptions): Promise<WaitForPushCardEvent> {
        return this.waitForPickCard(options)
    }

}
